/**
 * Copyright 2011 Joe Choi, All rights reserved.
 */

package tao.calendar.gregorian;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

import tao.calendar.gregorian.GregorianDate.Era;

/**
 * The Gregorian class is designed as a facade for manipulating object of
 * Time, Date and DateTime of Gregorian calendar system
 * 
 * @author Joe Choi
 * @version 1.0.0
 */
public class Gregorian {
	private Gregorian() {}
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////
	// GregorianTime section
	//////////////////////////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Create a GregoiranTime object from scalar
	 * @see GregorianTime
	 */
	/*
	public static GregorianTime time(final long scalar) {
		return(new ConcreteGregorianTime(scalar));
	}
	*/
	
	/**
	 * Create a GregoiranTime object from a time
	 * @see GregorianTime
	 */	
	public static GregorianTime time(final GregorianTime time) {
		return(new ConcreteGregorianTime(time));
	}

	/**
	 * Create a GregoiranTime object from a GregorianDateTime object
	 * @see GregorianDateTime
	 * @see GregorianTime
	 */
	public static GregorianTime time(final GregorianDateTime time) {
		return(new ConcreteGregorianTime(time.scalar()));
	}
	
	/**
	 * Create a GregoiranTime object by specifying hour, minute and second with zero millisecond
	 * @see GregorianTime
	 */	
	public static GregorianTime time(final int hour, final int minute, final int second) {
		return(new ConcreteGregorianTime(hour, minute, second, 0));
	}
	
	/**
	 * Create a GregoiranTime object by specifying hour, minute, second and millisecond
	 * @see GregorianTime
	 */	
	public static GregorianTime time(final int hour, final int minute, final int second, final int millis) {
		return(new ConcreteGregorianTime(hour, minute, second, millis));
	}
	
	/**
	 * Get a GregoiranTime object that can be modified
	 */		
	public static ModifiableTime getModifier(final GregorianTime time) {
		return(new ConcreteModifiableTime(time));
	}

	/**
	 * Get operator for time
	 */
	public static OperatableTime getOperator(final GregorianTime time) {
		return(new ConcreteOperatableTime(time));
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////
	// GregorianDate section
	//////////////////////////////////////////////////////////////////////////////////////////////////////	
	
	/**
	 * Create a GregoiranDate object from scalar
	 * @see GregorianDate
	 */
	/*
	public static GregorianDate date(final long scalar) {
		return(new ConcreteGregorianDate(scalar));
	}
	*/

	/**
	 * Create a GregoiranDate object from a date
	 * @see GregorianDate
	 */
	public static GregorianDate date(final GregorianDate date) {
		return(new ConcreteGregorianDate(date));
	}	

	/**
	 * Create a GregoiranDate object from a GregorianDateTime object
	 * @see GregorianDateTime
	 * @see GregorianDate
	 */
	public static GregorianDate date(final GregorianDateTime dt) {
		return(new ConcreteGregorianDate(dt.scalar()));
	}
	
	/**
	 * Create a GregoiranDate object by specify year, month and day with Era AD
	 * @see GregorianDate
	 */
	public static GregorianDate date(final int year, final int month, final int day) {
		return(new ConcreteGregorianDate(year, month, day));
	}	

	/**
	 * Create a GregoiranDate object by specify ear, year, month and day
	 * @see GregorianDate
	 */
	public static GregorianDate date(final Era era, final int year, final int month, final int day) {
		return(new ConcreteGregorianDate(era, year, month, day));
	}	
	
	/**
	 * Get a GregoiranTime object that can be modified
	 */		
	public static ModifiableDate getModifier(final GregorianDate date) {
		return(new ConcreteModifiableDate(date));
	}

	/**
	 * Get operator for date
	 */
	public static OperatableDate getOperator(final GregorianDate date) {
		return(new ConcreteOperatableDate(date));
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////
	// GregorianDateTime section
	//////////////////////////////////////////////////////////////////////////////////////////////////////		
	
	/*
	public static GregorianDateTime dateTime(final long scalar) {
		return(new ConcreteGregorianDateTime(scalar));
	}
	*/
	
	/**
	 * Create a GregoiranDate object from other GregorianDateTime object
	 * @see GregorianDateTime
	 */
	public static GregorianDateTime dateTime(final GregorianDateTime dt) {
		return(new ConcreteGregorianDateTime(dt));
	}

	/**
	 * Create a GregoiranDate object from a date
	 * @see GregorianDateTime
	 * @see GregorianDate
	 */
	public static GregorianDateTime dateTime(final GregorianDate date) {
		return(new ConcreteGregorianDateTime(date, time(0, 0, 0)));
	}	
	
	/**
	 * Create a GregoiranDate object from a date and time
	 * @see GregorianDateTime
	 * @see GregorianDate
	 * @see GregorianTime
	 */
	public static GregorianDateTime dateTime(final GregorianDate date, final GregorianTime time) {
		return(new ConcreteGregorianDateTime(date, time));
	}	
	
	/**
	 * Create a GregoiranDate object by specify the year, month, day, 
	 * hour, minute and second with zero millisecond
	 * @see GregorianDateTime
	 */
	public static GregorianDateTime dateTime(final int year, final int month, final int day,
									final int hour, final int minute, final int second) {
		return(dateTime(year, month, day, hour, minute, second, 0));
	}	
	
	/**
	 * Create a GregoiranDate object by specify the year, month, day, 
	 * hour, minute, second and millisecond
	 * @see GregorianDateTime
	 */
	public static GregorianDateTime dateTime(final int year, final int month, final int day,
									final int hour, final int minute, final int second, int ms) {
		return(new ConcreteGregorianDateTime(year, month, day, hour, minute, second, ms));
	}	
	
	/**
	 * Create a GregoiranDate object by specify the ear, year, month, day, 
	 * hour, minute and second with zero millisecond
	 * @see GregorianDateTime
	 */
	public static GregorianDateTime dateTime(final Era era, final int year, final int month, final int day,
									final int hour, final int minute, final int second) {
		return(dateTime(era, year, month, day, hour, minute, second, 0));
	}
	
	/**
	 * Create a GregoiranDate object by specify ear, year, month, day, 
	 * hour, minute, second and millisecond
	 * @see GregorianDateTime
	 */
	public static GregorianDateTime dateTime(final Era era, final int year, final int month, final int day,
									final int hour, final int minute, final int second, final int millis) {
		return(new ConcreteGregorianDateTime(era, year, month, day, hour, minute, second, millis));
	}

	/**
	 * Get a GregoiranDateTime object that can be modified
	 */		
	public static ModifiableDateTime getModifier(final GregorianDateTime date) {
		return(new ConcreteModifiableDateTime(date));
	}
	
	/**
	 * Get operator for date and time
	 */
	public static OperatableDateTime getOperator(final GregorianDateTime dt) {
		return(new ConcreteOperatableDateTime(dt));
	}
	
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Java Adapter Section
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 * Create a GregorianTime object from calendar
	 * @see GregorianTime
	 */
	public static GregorianTime time(Calendar calendar) {
		return(time(calendar.get(Calendar.HOUR_OF_DAY), 
					calendar.get(Calendar.MINUTE),
					calendar.get(Calendar.SECOND), 
					calendar.get(Calendar.MILLISECOND)));
	}
	
	/**
	 * Create a GregorianDate object from calendar
	 * @see GregorianDate
	 */
	public static GregorianDate date(Calendar calendar) {
		return(date(calendar.get(Calendar.ERA) == GregorianCalendar.AD ? Era.AD : Era.BC, 
					calendar.get(Calendar.YEAR),
					calendar.get(Calendar.MONTH) + 1, 
					calendar.get(Calendar.DAY_OF_MONTH)));
	}

	/**
	 * Create a GregorianDateTime object from calendar
	 * @see GregorianDateTime
	 */
	public static GregorianDateTime dateTime(Calendar calendar) {
		return(dateTime(
				calendar.get(Calendar.ERA) == GregorianCalendar.AD ? Era.AD : Era.BC, 
				calendar.get(Calendar.YEAR),
				calendar.get(Calendar.MONTH) + 1, 
				calendar.get(Calendar.DAY_OF_MONTH),
				calendar.get(Calendar.HOUR_OF_DAY), 
				calendar.get(Calendar.MINUTE),
				calendar.get(Calendar.SECOND), 
				calendar.get(Calendar.MILLISECOND)));
	}	
	
	/**
	 * Create a Calendar object from date with default Locale and TimeZone
	 * @see java.util.Calendar
	 * @see java.util.Locale
	 * @see java.util.TimeZone
	 * @see GregorianDate
	 */
	public static Calendar toCalendar(GregorianDate date) {
		return(toCalendar(date, TimeZone.getDefault(), Locale.getDefault()));
	}

	/**
	 * Create a Calendar object from date with default Locale
	 * @see java.util.Calendar
	 * @see java.util.Locale
	 * @see java.util.TimeZone
	 * @see GregorianDate
	 */
	public static Calendar toCalendar(GregorianDate date, TimeZone zone) {
		return(toCalendar(date, zone, Locale.getDefault()));
	}
	
	/**
	 * Create a Calendar object from date
	 * @see java.util.Calendar
	 * @see java.util.Locale
	 * @see java.util.TimeZone
	 * @see GregorianDate
	 */
	public static Calendar toCalendar(GregorianDate date, TimeZone zone, Locale locale) {
		Calendar calendar = Calendar.getInstance(zone, locale);
		calendar.set(Calendar.ERA, (date.getEra() == Era.AD ? GregorianCalendar.AD : GregorianCalendar.BC));
		calendar.set(date.getYear(), date.getMonth() - 1 , date.getDay(), 0, 0, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return(calendar);
	}

	/**
	 * Create a Calendar object from date and time with default Locale and TimeZone
	 * @see java.util.Calendar
	 * @see java.util.Locale
	 * @see java.util.TimeZone
	 * @see GregorianDateTime
	 */
	public static Calendar toCalendar(GregorianDateTime dt) {
		return(toCalendar(dt, TimeZone.getDefault(), Locale.getDefault()));
	}

	/**
	 * Create a Calendar object from date and time with default Locale
	 * @see java.util.Calendar
	 * @see java.util.Locale
	 * @see java.util.TimeZone
	 * @see GregorianDateTime
	 */
	public static Calendar toCalendar(GregorianDateTime dt, TimeZone zone) {
		return(toCalendar(dt, zone, Locale.getDefault()));
	}
	
	/**
	 * Create a Calendar object from date and time
	 * @see java.util.Calendar
	 * @see java.util.Locale
	 * @see java.util.TimeZone
	 * @see GregorianDateTime
	 */
	public static Calendar toCalendar(GregorianDateTime dt, TimeZone zone, Locale locale) {
		Calendar calendar = Calendar.getInstance(zone, locale);
		calendar.set(Calendar.ERA, (dt.getEra() == Era.AD ? GregorianCalendar.AD : GregorianCalendar.BC));
		calendar.set(dt.getYear(), dt.getMonth() - 1 , dt.getDay(), dt.getHour(), dt.getMinute(), dt.getSecond());
		calendar.set(Calendar.MILLISECOND, dt.getMillisecond());
		return(calendar);
	}
	
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Amount Section
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Create an Amount of year. Such amount is as a parameter for operation of GregorianDate and 
	 * GregorianDateTime
	 * 
	 * @see GregorianDate
	 * @see GregorianDateTime
	 */
	public static GregorianUnitAmount year(int amt) {
		return(new GregorianUnitAmount(Unit.Year, amt));
	}

	/**
	 * Create an Amount of month. Such amount is as a parameter for operation of GregorianDate and 
	 * GregorianDateTime 
	 * 
	 * @see GregorianDate
	 * @see GregorianDateTime
	 */
	public static GregorianUnitAmount month(int amt) {
		return(new GregorianUnitAmount(Unit.Month, amt));
	}

	/**
	 * Create an Amount of day. Such amount is as a parameter for operation of GregorianDate and 
	 * GregorianDateTime 
	 * 
	 * @see GregorianDate
	 * @see GregorianDateTime
	 */
	public static GregorianUnitAmount day(int amt) {
		return(new GregorianUnitAmount(Unit.Day, amt));
	}
	
	/**
	 * Create an Amount of hour. Such amount is as a parameter for operation of GregorianTime and 
	 * GregorianDateTime 
	 * 
	 * @see GregorianTime
	 * @see GregorianDateTime
	 */
	public static GregorianUnitAmount hour(int amt) {
		return(new GregorianUnitAmount(Unit.Hour, amt));
	}
	
	/**
	 * Create an Amount of minute. Such amount is as a parameter for operation of GregorianTime and 
	 * GregorianDateTime  
	 * 
	 * @see GregorianTime
	 * @see GregorianDateTime
	 */
	public static GregorianUnitAmount minute(int amt) {
		return(new GregorianUnitAmount(Unit.Minute, amt));
	}

	/**
	 * Create an Amount of second. Such amount is as a parameter for operation of GregorianTime and 
	 * GregorianDateTime 
	 * 
	 * @see GregorianTime
	 * @see GregorianDateTime
	 */
	public static GregorianUnitAmount second(int amt) {
		return(new GregorianUnitAmount(Unit.Second, amt));
	}
	
	/**
	 * Create an Amount of millisecond. Such amount is as a parameter for operation of GregorianTime and 
	 * GregorianDateTime 
	 * 
	 * @see GregorianTime
	 * @see GregorianDateTime
	 */
	public static GregorianUnitAmount millisecond(int amt) {
		return(new GregorianUnitAmount(Unit.Millisecond, amt));
	}

	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Amounts Section
	/////////////////////////////////////////////////////////////////////////////////////////////////////////	
	static GregorianAmount timeAmount(int hr, int min, int sec, int ms) {
		return(new GregorianAmount(hour(hr), minute(min), second(sec), millisecond(ms)));		
	}
	
	static GregorianAmount dateAmount(int year, int month, int day) {
		return(new GregorianAmount(year(year), month(month), day(day)));		
	}
	
	static GregorianAmount dateTimeAmount(int year, int month, int day, int hr, int min, int sec, int ms) {
		return(
			new GregorianAmount(
				hour(hr), minute(min), second(sec), millisecond(ms),
				year(year), month(month), day(day)
		));		
	}
	
	/**
	 * The unit of date and time of Gregorian calendar system
	 * 
	 * @author Joe Choi
	 * @version 1.0.0
	 */
	public enum Unit {
		Year, Month, Day, Hour, Minute, Second, Millisecond
	}	
}
